package com.incar.chuxing.utils.ble;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;

import com.google.gson.Gson;
import com.incar.chuxing.BuildConfig;
import com.incar.chuxing.api.ApiStore;
import com.incar.chuxing.api.UrlContainer;
import com.incar.chuxing.application.MyApplication;
import com.incar.chuxing.bean.CarBleBean;
import com.incar.chuxing.bean.CarBleYunCheBean;
import com.incar.chuxing.bean.CarBleZXTBean;
import com.incar.chuxing.bean.CarBsjBean;
import com.incar.chuxing.utils.Logger;
import com.incar.chuxing.utils.SPKey;
import com.incar.chuxing.utils.SPUtils;
import com.incar.chuxing.utils.ble.bean.CommandBleDbBean;
import com.incar.chuxing.utils.ble.bean.OrderCmd;
import com.incar.chuxing.utils.ble.callback.IConnectCallback;
import com.incar.chuxing.utils.ble.callback.data.ICharacteristicCallback;
import com.incar.chuxing.utils.ble.callback.scan.PeriodScanCallback;
import com.incar.chuxing.utils.ble.enmu.ControlCmd;
import com.incar.chuxing.utils.ble.enmu.MsgSendType;
import com.incar.chuxing.utils.ble.exception.BleException;
import com.incar.chuxing.utils.ble.helper.ICallBackCmd;
import com.incar.chuxing.utils.ble.helper.IParseResultCallback;
import com.incar.chuxing.utils.ble.manager.BLESendManager;
import com.incar.chuxing.utils.ble.manager.DeviceControlManager;
import com.incar.chuxing.utils.ble.manager.ParseManager;
import com.incar.chuxing.utils.ble.model.BluetoothLeDevice;
import com.incar.chuxing.utils.ble.utils.HexUtil;
import com.inuker.bluetooth.library.ClientManager;
import com.inuker.bluetooth.library.connect.listener.BleConnectStatusListener;
import com.inuker.bluetooth.library.connect.listener.BluetoothStateListener;
import com.inuker.bluetooth.library.connect.options.BleConnectOptions;
import com.inuker.bluetooth.library.connect.response.BleNotifyResponse;
import com.inuker.bluetooth.library.search.SearchRequest;
import com.inuker.bluetooth.library.search.SearchResult;
import com.inuker.bluetooth.library.search.response.SearchResponse;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

import static com.incar.chuxing.utils.ble.BleConstants.CarType.ZHI_XIN_TONG;
import static com.incar.chuxing.utils.ble.BleConstants.ControlType.CLOSE_DOOR;
import static com.incar.chuxing.utils.ble.BleConstants.ControlType.CLOSE_DOOR_POWEROFF;
import static com.incar.chuxing.utils.ble.BleConstants.ControlType.GET_STATUS;
import static com.incar.chuxing.utils.ble.BleConstants.SendDateType.APPLY_CONTROL;
import static com.incar.chuxing.utils.ble.BleConstants.SendDateType.APPLY_PSW;
import static com.incar.chuxing.utils.ble.BleConstants.SendDateType.CONTROL_COMMAND;
import static com.incar.chuxing.utils.ble.BleConstants.SendDateType.GET_TOKEN;
import static com.incar.chuxing.utils.ble.BleConstants.SendDateType.NO_RESPONSE_CMD;
import static com.incar.chuxing.utils.ble.BleConstants.SendDateType.VERIFY_PSW;
import static com.inuker.bluetooth.library.Constants.REQUEST_FAILED;
import static com.inuker.bluetooth.library.Constants.REQUEST_SUCCESS;
import static com.inuker.bluetooth.library.Constants.REQUEST_TIMEDOUT;

/**
 * Created by MichaelPi on 2018/1/18.
 */

public class BluetoothHelper implements BleNotifyResponse, ICallBackCmd {
    public static final int REQUEST_OPEN_BLE_REPEAT = 1;
    public static final int REQUEST_OPEN_BLE_ONCE = 2;
    public static final int REQUEST_OPEN_BLE = 3;
    private LinkedBlockingDeque<OrderCmd> unCompleteGroupCmd = new LinkedBlockingDeque<>();

    private String btMac;
    private String operateSerial = "";
    private BluetoothLeDevice mDevice;
    private boolean isDeviceFound;
    /**
     * 蓝牙扫描成功或失败的回调
     */
    private PeriodScanCallback periodScanCallback;
    public BleConstants.CarType carType;
    private BleConstants.ControlType controlType;
    private String carBlePsw;//密码
    private String carBleKey;//密钥
    public String carBleName;//蓝牙名字或蓝牙MAC, 无： 无-
    private UUID readDataUUID;
    private UUID readServerUUID;
    private UUID writeDataUUID;
    private UUID writeServerUUID;
    private static BluetoothHelper bluetoothHelper;
    private OnOperateCarListener onOperateCarListener;
    private BothConnectResponse connectResponse;
    private BleNotifyResponse bleNotifyResponse = this;
    private ICallBackCmd iCallBackCmd = this;
    private Disposable timeoutDisposable;
    private int connectTimes;//剩余的连接次数

    public static BluetoothHelper getInstance() {
        if (bluetoothHelper == null) {
            synchronized (BluetoothHelper.class) {
                if (bluetoothHelper == null) {
                    bluetoothHelper = new BluetoothHelper();
                }
            }
        }
        return bluetoothHelper;
    }

    public boolean isCarSupportBle(Context mContext) {
        carBlePsw = SPUtils.getInstance(mContext).getStringValue(SPKey.CAR_BLE_PSW);
        carBleName = SPUtils.getInstance(mContext).getStringValue(SPKey.BLE_MAC_IDC);
        carBleKey = SPUtils.getInstance(mContext).getStringValue(SPKey.CAR_BLE_KEY);
        return (null != carBleName && !"".equals(carBleName) && null != carBlePsw && !"".equals(carBlePsw));
    }

    public void removeBleInfo(Context mContext) {
        carBleName = null;
        carBlePsw = null;
        carBleKey = null;
        SPUtils.getInstance(mContext).removeValue(SPKey.CAR_BLE_PSW);
        SPUtils.getInstance(mContext).removeValue(SPKey.BLE_MAC_IDC);
        SPUtils.getInstance(mContext).removeValue(SPKey.CAR_BLE_KEY);
    }


    public boolean isBleSupported() {
        return ClientManager.getClient().isBleSupported();
    }

    public boolean isBluetoothOpened() {
        return ClientManager.getClient().isBluetoothOpened();
    }

    /**
     * 第一种打开蓝牙方式
     * 1.无法获取用户点了允许还是拒绝
     * 2.只能注册广播监听蓝牙开启了
     */
    public void openBluetooth() {
        ClientManager.getClient().openBluetooth();
    }

    /**
     * 第二种打开蓝牙方式
     * 1.再当前Activity中获取是否允许了开蓝牙
     * 2.也可以注册广播监听蓝牙开启了
     */
    public void openBluetooth(Activity activity, int requestCode) {
        Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        activity.startActivityForResult(intent, requestCode);
    }


    /**
     * 注册蓝牙开关监听
     */
    public void registerBluetoothStateListener(BluetoothStateListener bluetoothStateListener) {
        ClientManager.getClient().registerBluetoothStateListener(bluetoothStateListener);
    }

    /**
     * 解注册蓝牙开关监听
     */
    public void unregisterBluetoothStateListener() {
        ClientManager.getClient().unregisterBluetoothStateListener();
    }

    /**
     * 第一步
     * 搜索蓝牙设备
     */
    public void searchDevice(int scanTimes, final BothSearchResponse searchResponse, String operateSerial) {
        this.operateSerial = operateSerial;
        isDeviceFound = false;
        if (carType != ZHI_XIN_TONG) {//云车智慧和博实结的搜索方式
            SearchRequest request;
            request = new SearchRequest.Builder().searchBluetoothLeDevice(10000, scanTimes).build();
            ClientManager.getClient().search(request, new SearchResponse() {
                @Override
                public void onSearchStarted() {
                    Logger.i("bleinfo-Helper", "start scan-Y/B");
                }

                @Override
                public void onDeviceFounded(SearchResult device) {
                    Logger.d("bleinfo-Helper", "扫描到一个设备 " + device.rssi + "   " + device.getName() + "  " + device.getAddress());
                    if (!isDeviceFound && carBleName != null
                            && carBleName.equals(device.getAddress().replace(":", "").toUpperCase())) {
                        isDeviceFound = true;
                        stopScanDevice();
                        btMac = device.getAddress();
                        searchResponse.onSearchSuccess(device.getAddress(), device.getName(), operateSerial);
                    }

                }

                @Override
                public void onSearchStopped() {
                    searchResponse.onSearchTimeout(operateSerial);
                }

                @Override
                public void onSearchCanceled() {

                }

                @Override
                public void onSearchTimeOutEveryTime() {
                    searchResponse.onSearchTimeoutEveryTime(operateSerial);
                }
            });
        } else {//智信通的搜索方式
            ZXTScan(searchResponse, scanTimes);
        }
    }

    private void ZXTScan(BothSearchResponse searchResponse, int scanTimes) {
        Logger.i("bleinfo-Helper", "start scan-ZXT");
        ViseBluetooth.getInstance().setScanTimeout(10000).startScan(periodScanCallback = new PeriodScanCallback() {
            @Override
            public void scanTimeout() {
                //告诉Activity扫描超时了
                if (scanTimes - 1 > 0) {
                    if (searchResponse != null) {//单次扫描超时
                        searchResponse.onSearchTimeoutEveryTime(operateSerial);
                    }
                    ZXTScan(searchResponse, scanTimes - 1);
                } else if (searchResponse != null) {//尝试times次后，扫描失败
                    searchResponse.onSearchTimeout(operateSerial);
                }
            }

            @Override
            public void onDeviceFound(final BluetoothLeDevice device) {
                Logger.d("bleinfo-Helper", "扫描到一个设备 " + device.getRssi() + "   " + device.getName() + "  " + device.getAddress());

                if (!isDeviceFound && device != null && carBleName != null && carBleName.equals(device.getName())) {
                    isDeviceFound = true;
                    stopScanDevice();
                    mDevice = device;
                    Logger.i("bleinfo-Helper", "scanned-ZXT " + mDevice.getRssi() + "   " + mDevice.getName() + "  " + device.getAddress());
                    btMac = mDevice.getAddress();
                    //告诉Activity扫描到了
                    searchResponse.onSearchSuccess(mDevice.getAddress(), mDevice.getName(), operateSerial);
                }
            }
        });
    }


    /**
     * 停止搜索
     */
    public void stopScanDevice() {
        if (carType == ZHI_XIN_TONG) {
            if (periodScanCallback != null) {
                ViseBluetooth.getInstance().stopScan(periodScanCallback);
                Logger.i("bleinfo-Helper", "stop scan-ZXT");
            }
        } else {
            ClientManager.getClient().stopSearch();
            Logger.i("bleinfo-Helper", "stop scan-Y/B");
        }

    }


    /**
     * 第二步
     * 连接设备
     * times 重连接次数
     */
    public void connectDevice(BothConnectResponse connectResponse, int times) {
        this.connectResponse = connectResponse;
        connectTimes = times - 1;
        if (carType != ZHI_XIN_TONG) {
            Logger.i("bleinfo-Helper", "start connect-Y/B");
            BleConnectOptions options = new BleConnectOptions.Builder()
                    .setConnectRetry(1)
                    .setConnectTimeout(16000)
                    .setServiceDiscoverRetry(2)
                    .setServiceDiscoverTimeout(8000)
                    .build();
            //发起连接
            ClientManager.getClient().connect(btMac, options, (code, profile) -> {
                if (code == REQUEST_SUCCESS) {
                    Logger.i("bleinfo-Helper", "connect ok " + btMac);
                    if (carType == BleConstants.CarType.YUN_CHE) {
                        readServerUUID = BleConstants.YunChe.readServerUUID;
                        readDataUUID = BleConstants.YunChe.readDataUUID;
                        writeDataUUID = BleConstants.YunChe.writeDataUUID;
                        writeServerUUID = BleConstants.YunChe.writeServerUUID;
                        validatePsw();
                    } else if (carType == BleConstants.CarType.BO_SHI_JIE) {
                        //蓝牙密码和密钥转成byte[],并赋值到加解密工具类中BoShiJieAesUtils
                        BSJParseUtils.getInstance().parsePswKey(carBlePsw, carBleKey);
                        readServerUUID = BleConstants.BoShiJie.readServerUUID;
                        readDataUUID = BleConstants.BoShiJie.readDataUUID;
                        writeDataUUID = BleConstants.BoShiJie.writeDataUUID;
                        writeServerUUID = BleConstants.BoShiJie.writeServerUUID;
                        ClientManager.getClient().notify(btMac, readServerUUID, readDataUUID, bleNotifyResponse);
                        getToken();
                    }

                } else if (code == REQUEST_FAILED) {
                    if (connectTimes > 0) {
                        Logger.i("bleinfo-Helper", "single conn failure-Y/B");
                        if (connectResponse != null) {//单次连接超时
                            connectResponse.onConnectTimeoutEveryTime(operateSerial);
                        }
                        connectDevice(connectResponse, connectTimes);
                    } else if (connectResponse != null) {//尝试times次后，连接失败
                        Logger.i("bleinfo-Helper", "final conn failure-Y/B");
                        connectResponse.onConnectFailed("连接失败", "", operateSerial);
                    }
                }
            });
        } else {
            Logger.i("bleinfo-Helper", "start connect-ZXT");
            ViseBluetooth.getInstance().connect(mDevice, new IConnectCallback() {
                @Override
                public void onConnectSuccess(BluetoothGatt gatt, int status, int delayTime) {
                    BLESendManager.getInstance().setCallBackCmd(iCallBackCmd);
                    connectResponse.onConnectSuccess(operateSerial);
                }

                @Override
                public void onConnectFailure(BleException exception) {
                    if (connectTimes > 0) {
                        Logger.i("bleinfo-Helper", "single conn failure-ZXT");
                        if (connectResponse != null) {//单次连接超时
                            connectResponse.onConnectTimeoutEveryTime(operateSerial);
                        }
                        connectDevice(connectResponse, connectTimes);
                    } else if (connectResponse != null) {//尝试times次后，连接失败
                        Logger.i("bleinfo-Helper", "final conn failure-ZXT");
                        connectResponse.onConnectFailed("连接失败", "", operateSerial);
                    }
                }

                @Override
                public void onDisconnect() {

                }

                @Override
                public void onUnDiscoveredService() {

                }

                @Override
                public void onDisconnectByService() {

                }

                @Override
                public void onConnectTimeout() {
                    if (connectTimes > 0) {
                        Logger.i("bleinfo-Helper", "single conn timeout-ZXT");
                        if (connectResponse != null) {//单次连接超时
                            connectResponse.onConnectTimeoutEveryTime(operateSerial);
                        }
                        connectDevice(connectResponse, connectTimes);
                    } else if (connectResponse != null) {//尝试times次后，连接失败
                        Logger.i("bleinfo-Helper", "final conn timeout-ZXT");
                        connectResponse.onConnectFailed("连接失败-超时", "", operateSerial);
                    }
                }
            }, bleCallback);
        }
    }


    public void registerConnectStatusListener(BleConnectStatusListener bleConnectStatusListener) {
        ClientManager.getClient().registerConnectStatusListener(btMac, bleConnectStatusListener);
    }

    public void unregisterConnectStatusListener(BleConnectStatusListener bleConnectStatusListener) {
        ClientManager.getClient().unregisterConnectStatusListener(btMac, bleConnectStatusListener);
    }

    /**
     * 主动获取当前的连接状态
     */

    public boolean isConnect() {
        if ((carType == BleConstants.CarType.YUN_CHE || carType == BleConstants.CarType.BO_SHI_JIE) && btMac != null) {
            return ClientManager.getClient().getConnectStatus(btMac) == 2;
        } else if (carType == ZHI_XIN_TONG) {
            return ViseBluetooth.getInstance().isConnected();
        }
        return false;
    }

    public void disConnectDevice() {
        if ((carType == BleConstants.CarType.YUN_CHE || carType == BleConstants.CarType.BO_SHI_JIE) && btMac != null) {
            Logger.i("bleinfo-Helper", "dis conn-Y/B");
            ClientManager.getClient().disconnect(btMac);
        } else if (carType == ZHI_XIN_TONG) {
            Logger.i("bleinfo-Helper", "dis conn-ZXT");
            ViseBluetooth.getInstance().clear();
        }
    }

    /**
     * 获得当前连接设备的mac
     */
    public String getCurrentConnectMac() {
        return btMac;
    }


    /**
     * *云车验证密码
     * *连接后延时100ms发送一次握手指令
     * *1s后再发送申请指令
     * *
     * *          0          1
     * *         握手   订阅notify/申请验证
     * *    |_____|__________|
     * *     100ms   1000ms
     * *
     */
    public void validatePsw() {
        Observable
                .interval(100, 1000, TimeUnit.MILLISECONDS)
                .take(2)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(times -> {
                    if (times == 0) {  //发送握手指令
                        Logger.i("bleinfo-Helper", "send hand-Y");
                        sendData(YunCheParseUtils.getInstance().createPswHandShakeCmd(), NO_RESPONSE_CMD);
                    } else if (times == 1) {//申请验证
                        ClientManager.getClient().notify(btMac, readServerUUID, readDataUUID, bleNotifyResponse);
                        sendData(YunCheParseUtils.getInstance().createApplyForVerifyPswCmd(), APPLY_PSW);
                    }
                });
    }


    /**
     * 博实结获取token
     */
    public void getToken() {
        Observable.timer(300, TimeUnit.MILLISECONDS).observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Long>() {
                    @Override
                    public void accept(Long aLong) throws Exception {
                        sendData(BSJParseUtils.getInstance().createGetTokenCmd(), GET_TOKEN);
                    }
                });
    }


    public void operateCar(BleConstants.ControlType controlType, OnOperateCarListener onOperateCarListener, String operateSerial) {
        this.onOperateCarListener = onOperateCarListener;//把回调赋给成员变量，当指令发送失败或发送超时，车机响应成功或失败的时候回调给发起指令的地方
        this.operateSerial = operateSerial;
        if (carType == BleConstants.CarType.YUN_CHE) {
            this.controlType = controlType;//初始化车辆控制指令的类型
            operateYunCheCar();
        } else if (carType == BleConstants.CarType.BO_SHI_JIE) {
            this.controlType = controlType;//初始化车辆控制指令的类型
            operateBoShiJieCar();
        } else if (carType == ZHI_XIN_TONG) {
            String cmd = null;
            MsgSendType msgSendType = MsgSendType.MSG_CONTROL;
            if (controlType != null) {
                switch (controlType) {
                    case OPEN_DOOR:
                        cmd = ControlCmd.CONTROL_OPEN_DOOR_POWER.getCmd();
                        msgSendType = MsgSendType.MSG_CONTROL;
                        break;
                    case CLOSE_DOOR:
                        cmd = ControlCmd.CONTROL_CLOSE_DOOR.getCmd();
                        msgSendType = MsgSendType.MSG_CONTROL;
                        break;
                    case CLOSE_DOOR_POWEROFF:
                        cmd = ControlCmd.CONTROL_CLOSE_DOOR_OUTAGE.getCmd();
                        msgSendType = MsgSendType.MSG_CONTROL;
                        break;
                    case GET_STATUS:
                        cmd = "";
                        msgSendType = MsgSendType.MSG_STATE;
                        reSetBleManager();
                        reSetParseManager();
                        OrderCmd nextCmd = initOrderCmd("", "", MsgSendType.MSG_CAR);
                        if (nextCmd != null) {
                            unCompleteGroupCmd.add(nextCmd);
                        }
                        break;
                }
            }

            reSetBleManager();
            reSetParseManager();
            OrderCmd currentOrderCmd = initOrderCmd(cmd, carBlePsw, msgSendType);
            if (currentOrderCmd != null) {
                BLESendManager.getInstance().send(currentOrderCmd, true);
            } else {
                onOperateCarListener.onOperateFailure("currentOrderCmd为null", "", operateSerial);
            }
        }


    }

    public void operateYunCheCar() {
        Observable
                .interval(300, TimeUnit.MILLISECONDS)
                .take(2)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(times -> {
                    if (times == 0) {  //发送唤醒指令
                        sendData(YunCheParseUtils.getInstance().createWakeCmd(), NO_RESPONSE_CMD);
                    } else if (times == 1) {//发送申请控制/申请获取数据
                        sendData(YunCheParseUtils.getInstance().createApplyForControlCmd(), APPLY_CONTROL);
                    }
                });
    }


    public void operateBoShiJieCar() {
        if (controlType != null) {
            switch (controlType) {
                case OPEN_DOOR:
                    sendData(BSJParseUtils.getInstance().createOpenCmd(), CONTROL_COMMAND);
                    break;
                case CLOSE_DOOR:
                    sendData(BSJParseUtils.getInstance().createCloseCmd(), CONTROL_COMMAND);
                    break;
                case CLOSE_DOOR_POWEROFF:
                    sendData(BSJParseUtils.getInstance().createCloseCmd(), CONTROL_COMMAND);
                    break;
                case GET_STATUS:
                    sendData(BSJParseUtils.getInstance().createStatusCmd(), CONTROL_COMMAND);
                    break;
            }
        }
    }


    /**
     * 向锁发送指令
     */
    private void sendData(byte[] data, BleConstants.SendDateType sendDateType) {
        if (BuildConfig.DEBUG) {
            Logger.i("bleinfo-Helper", "send Data" + String.valueOf(HexUtil.encodeHex(data)));
        } else {
            Logger.i("bleinfo-Helper", "send Data");
        }

        ClientManager.getClient().write(btMac, writeServerUUID, writeDataUUID, data, code -> {
            if (code == REQUEST_SUCCESS) {
                Logger.i("bleinfo-Helper", "send Data ok");
            } else if (code == REQUEST_FAILED) {
                Logger.i("bleinfo-Helper", "send Data failure");
                if (onOperateCarListener != null) {
                    onOperateCarListener.onOperateFailure("发送失败", "", operateSerial);//指令发送失败，通知发起指令的地方
                }
            } else if (code == REQUEST_TIMEDOUT) {
                Logger.i("bleinfo-Helper", "send Data timeout");
                if (onOperateCarListener != null) {
                    onOperateCarListener.onOperateFailure("发送超时", "", operateSerial);//指令发送超时，通知发起指令的地方
                }
            }
        });
        if (sendDateType != NO_RESPONSE_CMD) {//需要主机响应的指令，注册超时
            Logger.d("bleinfo-Helper", "register cmd timeout" + sendDateType.name());
            registerResponseTimeout(sendDateType);
        }
    }


    /**
     * 发送指令4秒未响应超时
     */
    private void registerResponseTimeout(final BleConstants.SendDateType sendDateType) {
        if (timeoutDisposable != null) {
            timeoutDisposable.dispose();
            timeoutDisposable = null;
        }
        timeoutDisposable = Observable.timer(4, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(aLong -> {
                    switch (sendDateType) {
                        case APPLY_PSW:
                            disConnectDevice();
                            if (connectTimes > 0) {
                                Logger.i("bleinfo-Helper", "single conn failure- apply psw timeout");
                                if (connectResponse != null) {//单次连接超时
                                    connectResponse.onConnectTimeoutEveryTime(operateSerial);
                                }
                                connectDevice(connectResponse, connectTimes);
                            } else if (connectResponse != null) {//尝试times次后，连接失败
                                Logger.i("bleinfo-Helper", "final conn failure- apply psw timeout");
                                connectResponse.onConnectFailed("申请验证码的响应超时", "", operateSerial);
                            }

                            break;
                        case VERIFY_PSW:
                            Logger.i("bleinfo-Helper", "验证密码超时" + sendDateType.name());
                            disConnectDevice();
                            if (connectTimes > 0) {
                                Logger.i("bleinfo-Helper", "single conn failure- certify psw timeout");
                                if (connectResponse != null) {//单次连接超时
                                    connectResponse.onConnectTimeoutEveryTime(operateSerial);
                                }
                                connectDevice(connectResponse, connectTimes);
                            } else if (connectResponse != null) {//尝试times次后，连接失败
                                Logger.i("bleinfo-Helper", "final conn failure- certify psw timeout");
                                connectResponse.onConnectFailed("验证密码超时", "", operateSerial);
                            }
                            break;
                        case APPLY_CONTROL:
                            if (onOperateCarListener != null) {
                                onOperateCarListener.onOperateFailure("申请控制的响应超时", "", operateSerial);
                            }
                            break;
                        case GET_TOKEN:
                            disConnectDevice();
                            if (connectTimes > 0) {
                                Logger.i("bleinfo-Helper", "single conn failure- Y get token timeout");
                                if (connectResponse != null) {//单次连接超时
                                    connectResponse.onConnectTimeoutEveryTime(operateSerial);
                                }
                                connectDevice(connectResponse, connectTimes);
                            } else if (connectResponse != null) {//尝试times次后，连接失败
                                Logger.i("bleinfo-Helper", "final conn failure- Y get token timeout");
                                connectResponse.onConnectFailed("博实结获取token超时", "", operateSerial);
                            }
                            break;
                        case CONTROL_COMMAND:
                            if (onOperateCarListener != null) {
                                onOperateCarListener.onOperateFailure("控制的响应超时", "", operateSerial);
                            }
                            break;
                    }
                });
    }


    /**
     * 分包发送
     */
    private void splitWrite(final String mac, final UUID writeServerUUID, final UUID writeDataUUID, final Queue<byte[]> dataInfoQueue) {
        if (dataInfoQueue.peek() != null) {
            final byte[] data = dataInfoQueue.poll();
            //发送命令
            ClientManager.getClient().write(mac, writeServerUUID, writeDataUUID, data, code -> {
                if (code == REQUEST_SUCCESS) {
                    Logger.i("bleinfo-Helper", "发送成功 分包" + data.length + "   " + Arrays.toString(data));
                    splitWrite(mac, writeServerUUID, writeDataUUID, dataInfoQueue);
                } else if (code == REQUEST_FAILED) {
                    Logger.i("bleinfo-Helper", "发送失败" + data.length);
                    if (onOperateCarListener != null) {
                        onOperateCarListener.onOperateFailure("发送失败", "", operateSerial);//指令发送失败，通知发起指令的地方
                    }
                } else if (code == REQUEST_TIMEDOUT) {
                    Logger.i("bleinfo-Helper", "发送超时" + data.length);
                    if (onOperateCarListener != null) {
                        onOperateCarListener.onOperateFailure("发送超时", "", operateSerial);//指令发送超时，通知发起指令的地方
                    }
                }
            });
        }
    }


    /**
     * 云车智慧接收到数据的回调
     */
    @Override
    public void onNotify(UUID service, UUID character, byte[] rawData) {
        Logger.i("bleinfo-Helper", "notify raw data" + Arrays.toString(rawData));
        if (carType == BleConstants.CarType.YUN_CHE && rawData[0] == (byte) 0xBD) {//云车
            if (timeoutDisposable != null) {
                timeoutDisposable.dispose();
                timeoutDisposable = null;
            }
            switch (rawData[2]) {
                //主机应答申请验证密码指令,AES1解密
                case (byte) 0xB3:
                    Logger.i("bleinfo-Helper", "response apply psw");
                    YunCheParseUtils.getInstance().aes1Decrypt(rawData);
                    sendData(YunCheParseUtils.getInstance().createVerifyPswCmd(carBlePsw), VERIFY_PSW);
                    break;
                //主机应答验证密码指令
                case (byte) 0xB6:
                    //密码正确
                    if (rawData[3] == 0x01) {
                        Logger.i("bleinfo-Helper", "response psw ok");
                        if (connectResponse != null) {
                            connectResponse.onConnectSuccess(operateSerial);
                        }
                    }
                    //密码错误
                    else {
                        Logger.i("bleinfo-Helper", "response psw err  " + rawData[3]);
                        if (connectResponse != null) {
                            connectResponse.onConnectFailed("连接失败，密码错误", Arrays.toString(rawData), operateSerial);
                        }
                    }
                    break;
                //主机应答申请控制/获取数据指令,AES1解密
                case (byte) 0xA3:
                    Logger.i("bleinfo-Helper", "response apply control/status ok");
                    YunCheParseUtils.getInstance().aes1Decrypt(rawData);
                    sendData(YunCheParseUtils.getInstance().createControlCmd(controlType), CONTROL_COMMAND);
                    break;
                //主机应答控制指令，明文
                case (byte) 0xA6:
                    //操作成功
                    if (rawData[3] == 0x01) {//开关门执行成功
                        Logger.i("bleinfo-Helper", "response control ok");
                        onOperateCarListener.onOperateSuccess(null, Arrays.toString(rawData), operateSerial);
                    }
                    //操作失败
                    else if (rawData[3] == 0x03) {//开关门执行失败
                        Logger.i("bleinfo-Helper", "response control err-0x03");
                        onOperateCarListener.onOperateFailure("操作失败", Arrays.toString(rawData), operateSerial);
                    } else {
                        Logger.i("bleinfo-Helper", "response control err-else");
                        onOperateCarListener.onOperateFailure("操作失败", Arrays.toString(rawData), operateSerial);
                    }
                    break;

                case (byte) 0xA8:
                    Logger.i("bleinfo-Helper", "response status ok");
                    onOperateCarListener.onOperateSuccess(YunCheParseUtils.getInstance().parseStatus(rawData), Arrays.toString(rawData), operateSerial);
                    break;
            }
        } else if (carType == BleConstants.CarType.BO_SHI_JIE) {
            byte[] data = BSJParseUtils.getInstance().Decrypt(rawData);
            Logger.d("bleinfo-Helper", "notify解析后" + String.valueOf(HexUtil.encodeHex(data)));
            if (!(data[0] == (byte) 0xCB && data[4] == 0x00)) {
                if (timeoutDisposable != null) {
                    timeoutDisposable.dispose();
                    timeoutDisposable = null;
                }
            }

            if (data[0] == (byte) 0xCB && data[4] == 0x00) {//命令送达响应
                Logger.i("bleinfo-Helper", "命令" + data[1] + " " + data[2] + "正确送达");
            } else if (data[0] == 0x06 && data[1] == 0x02) {//接收到token
                Logger.i("bleinfo-Helper", "response got token");
                if (connectResponse != null) {
                    if (BSJParseUtils.getInstance().parseToken(data)) {
                        connectResponse.onConnectSuccess(operateSerial);
                    } else {
                        disConnectDevice();
                        if (connectTimes > 0) {
                            Logger.i("bleinfo-Helper", "single conn failure got token-parse err");
                            if (connectResponse != null) {//单次连接超时
                                connectResponse.onConnectTimeoutEveryTime(operateSerial);
                            }
                            connectDevice(connectResponse, connectTimes);
                        } else if (connectResponse != null) {//尝试times次后，连接失败
                            Logger.i("bleinfo-Helper", "final conn failure got token-parse err");
                            connectResponse.onConnectFailed("获得了token,解析失败", Arrays.toString(data), operateSerial);
                        }
                    }
                }
            } else if (data[0] == 0x05 && data[1] == 0x02 && data[3] == 0x00) {//开门成功
                Logger.i("bleinfo-Helper", "response open ok");
                sendData(BSJParseUtils.getInstance().createPowerCmd(), CONTROL_COMMAND);
            } else if (data[0] == 0x05 && data[1] == 0x02 && data[3] == 0x01) {//开门失败
                Logger.i("bleinfo-Helper", "response open err");
                if (connectResponse != null) {
                    onOperateCarListener.onOperateFailure("开门失败", Arrays.toString(data), operateSerial);
                }
            } else if (data[0] == 0x05 && data[1] == 0x08 && data[3] == 0x00) {//关门成功
                Logger.i("bleinfo-Helper", "response close ok");
                //关门后查询车状态
                sendData(BSJParseUtils.getInstance().createStatusCmd(), CONTROL_COMMAND);
            } else if (data[0] == 0x05 && data[1] == 0x08 && data[3] == 0x01) {//关门失败
                Logger.i("bleinfo-Helper", "response close err");
                if (connectResponse != null) {
                    onOperateCarListener.onOperateFailure("关门失败", Arrays.toString(data), operateSerial);
                }
            } else if (data[0] == 0x08 && data[1] == 0x13 && data[3] == 0x00) {//加油电成功
                Logger.i("bleinfo-Helper", "response add oil ok");
                if (connectResponse != null) {
                    onOperateCarListener.onOperateSuccess(null, Arrays.toString(data), operateSerial);
                }
            } else if (data[0] == 0x08 && data[1] == 0x13 && data[3] == 0x01) {//加油电失败
                Logger.i("bleinfo-Helper", "response add oil err");
                if (connectResponse != null) {
                    onOperateCarListener.onOperateFailure("加油电失败", Arrays.toString(data), operateSerial);
                }
            } else if (data[0] == 0x08 && data[1] == 0x11 && data[3] == 0x00) {//断油电成功
                Logger.i("bleinfo-Helper", "response cut oil ok");
                if (connectResponse != null) {
                    onOperateCarListener.onOperateSuccess(null, Arrays.toString(data), operateSerial);
                }
            } else if (data[0] == 0x08 && data[1] == 0x11 && data[3] == 0x01) {//断油电失败
                Logger.i("bleinfo-Helper", "response cut oil err");
                if (connectResponse != null) {
                    onOperateCarListener.onOperateFailure("断油电失败", Arrays.toString(data), operateSerial);
                }
            } else if (data[0] == 0x02 && data[1] == 0x02) {//车辆状态
                if (data[2] == 0x01 && data[3] == (byte) 0xff) {
                    Logger.i("bleinfo-Helper", "response status err");
                    if (connectResponse != null) {
                        onOperateCarListener.onOperateFailure("查询状态失败", Arrays.toString(data), operateSerial);
                    }
                } else {
                    Logger.i("bleinfo-Helper", "response status ok");
                    if (connectResponse != null) {
                        if (controlType == GET_STATUS) {
                            onOperateCarListener.onOperateSuccess(BSJParseUtils.getInstance().parseStatus(data), Arrays.toString(data), operateSerial);
                        } else if (controlType == CLOSE_DOOR) {
                            if (BSJParseUtils.getInstance().parseDoorStatus(data)) {//查询到门全关了
                                onOperateCarListener.onOperateSuccess(null, Arrays.toString(data), operateSerial);
                            } else {
                                onOperateCarListener.onOperateFailure("有门未关闭", Arrays.toString(data), operateSerial);
                            }

                        } else if (controlType == CLOSE_DOOR_POWEROFF) {//关门后还要断电
                            if (BSJParseUtils.getInstance().parseDoorStatus(data)) {//查询到门全关了
                                sendData(BSJParseUtils.getInstance().createPoweroffCmd(), CONTROL_COMMAND);
                            } else {
                                onOperateCarListener.onOperateFailure("有门未关闭", Arrays.toString(data), operateSerial);
                            }
                        }
                    }
                }
            }
        }
    }


    @Override
    public void onResponse(int code) {
        //设置notify是否成功
    }


    //智信通接收蓝牙主机的响应
    private ICharacteristicCallback bleCallback = new ICharacteristicCallback() {
        @Override
        public void onSuccess(byte[] characteristic) {

            ParseManager.getInstance().parse(characteristic, new IParseResultCallback() {
                //收到完整指令
                @Override
                public void onSendNormalCmd(int serial) {
                    Logger.i("bleinfo-Helper", "send 6001");
                    BLESendManager.getInstance().send(setOrderCmd("", "", true, MsgSendType.MSG_NORMAL, DeviceControlManager.getInstance().getNormalCmd(serial)));
                }

                //收到6001
                @Override
                public void onCancleRepeatSend(int state, int serialNum) {
                    Logger.i("bleinfo-Helper", "cancel repeat send");
                    BLESendManager.getInstance().cancleRepeat();
                }


                //收到完整指令
                @Override
                public void parseEnd(CommandBleDbBean cmd, String sendCode, int controlResultCode) {
                    Logger.i("bleinfo-Helper", "notify parse end");
                    BLESendManager.getInstance().cancleTimeOut();
                    saveData(cmd, sendCode, controlResultCode);
                }

            });
            //zViseBluetooth.getInstance().onDataParserCompleted();
        }

        @Override
        public void onFailure(BleException exception) {
            if (exception == null) {
                return;
            }
            Logger.i("bleinfo-Helper", "notify failure:" + exception.getDescription());
        }
    };

    //蓝牙扫描结果的回调
    public interface BothSearchResponse {
        void onSearchSuccess(String mac, String bleName, String operateSerial);

        void onSearchTimeout(String operateSerial);

        void onSearchTimeoutEveryTime(String operateSerial);
    }

    //蓝牙连接结果的回调
    public interface BothConnectResponse {

        void onConnectSuccess(String operateSerial);

        void onConnectFailed(String error, String response, String operateSerial);

        void onConnectTimeoutEveryTime(String operateSerial);
    }

    //蓝牙命令操作的结果回调
    public interface OnOperateCarListener {
        /**
         * 发送指令后，车机响应操作成功
         */
        void onOperateSuccess(int[] status, String response, String operateSerial);

        /**
         * 发送失败或者超时，车机响应操作失败
         */
        void onOperateFailure(String error, String response, String operateSerial);

    }


    /**
     * 拆分数据
     */
    private Queue<byte[]> splitByte(byte[] data, int count) {
        Queue<byte[]> byteQueue = new LinkedList<>();
        if (data != null) {
            int index = 0;
            do {
                byte[] rawData = new byte[data.length - index];
                byte[] newData;
                System.arraycopy(data, index, rawData, 0, data.length - index);
                if (rawData.length <= count) {
                    newData = new byte[rawData.length];
                    System.arraycopy(rawData, 0, newData, 0, rawData.length);
                    index += rawData.length;
                } else {
                    newData = new byte[count];
                    System.arraycopy(data, index, newData, 0, count);
                    index += count;
                }
                byteQueue.offer(newData);
            } while (index < data.length);
        }
        return byteQueue;
    }


    /********************************************
     ********************************************
     *******以 下 是 智 信 通 蓝 牙 所 需 内 容*******
     ********************************************
     ********************************************/


    /**
     * @param sendCode          发送的控制指令，状态读取的时候是空值
     * @param controlResultCode 控制结果,超时是-1，状态读取时也是-1
     */

    private void saveData(CommandBleDbBean bleDbBean, String sendCode, int controlResultCode) {
        if (bleDbBean.getType() == 4) {
            //控制操作结果的回调
            String error = ZXTParseUtils.getInstance().parseControlResult(controlResultCode, sendCode);
            if (null == error) {
                onOperateCarListener.onOperateSuccess(null, "", operateSerial); //指令响应成功，通知发起指令的地方
            } else {
                Logger.i("bleinfo-Helper", "response control err result " + error);

                onOperateCarListener.onOperateFailure(error, "", operateSerial); //指令响应失败，通知发起指令的地方
            }
        } else if (bleDbBean.getType() == 1) {
            //gps
        } else if (bleDbBean.getType() == 2) {
            //车状态
            Logger.i("bleinfo-Helper", "response status ok");
            ZXTParseUtils.getInstance().parseCarStatus(bleDbBean);
        } else if (bleDbBean.getType() == 3) {
            //车里程
            Logger.i("bleinfo-Helper", "response mileage ok");
            onOperateCarListener.onOperateSuccess(ZXTParseUtils.getInstance().parseCarMileage(bleDbBean), "", operateSerial); //指令响应成功，通知发起指令的地方
        }
    }


    @Override
    public void onTimeOut(OrderCmd orderCmd, int type) {
        Logger.i("bleinfo-Helper", "send cmd timeout-ZXT");
        if (onOperateCarListener != null) {
            onOperateCarListener.onOperateFailure("指令响应发送超时", "", operateSerial); //指令响应失败，通知发起指令的地方
        }
    }

    @Override
    public void onNormalCmdSendEnd() {
        new Handler().postDelayed(() -> {
            if (!unCompleteGroupCmd.isEmpty()) {
                OrderCmd pool = unCompleteGroupCmd.poll();
                BLESendManager.getInstance().send(pool, true);
            }
        }, 100);

    }

    @Override
    public void onStartSend() {

    }

    private OrderCmd setOrderCmd(String cmd, String pwd, boolean isNormalCmd, MsgSendType type, OrderCmd orderCmd) {
        orderCmd.setType(type);
        orderCmd.setPwd(pwd);
        orderCmd.setCmdS(cmd);
        orderCmd.setNormalCode(isNormalCmd);
        return orderCmd;
    }

    /**
     * 智信通生成指令
     */
    private OrderCmd initOrderCmd(String cmd, String pwd, MsgSendType type) {
        OrderCmd currentOrderCmd = null;
        if (type == MsgSendType.MSG_CONTROL) {
            currentOrderCmd = setOrderCmd(cmd, pwd, false, type, DeviceControlManager.getInstance()
                    .getDeviceControlCmd(cmd, pwd, ParseManager.getInstance().getmControlCmds()));
        } else if (type == MsgSendType.MSG_GPS) {
            currentOrderCmd = setOrderCmd(cmd, "", false, type, DeviceControlManager.getInstance()
                    .getGPSDataCmd());
        } else if (type == MsgSendType.MSG_STATE) {
            currentOrderCmd = setOrderCmd(cmd, "", false, type, DeviceControlManager.getInstance()
                    .getDeviceStateCmd());
        } else if (type == MsgSendType.MSG_CAR) {
            currentOrderCmd = setOrderCmd(cmd, "", false, type, DeviceControlManager.getInstance()
                    .getDeviceCarInfo());
        }
        return currentOrderCmd;
    }

    private void reSetBleManager() {
        BLESendManager.getInstance().reSet();
    }

    private void reSetParseManager() {
        ParseManager.getInstance().reSet();
    }


    public void getBleInfo(String carId, Context mContext, OnGetBleResponse onGetBleResponse) {
        ApiStore.getInstance().getApiService(UrlContainer.Request.BASE_GATE_URL).getCarBleInfo(((MyApplication) mContext.getApplicationContext()).getRequestFieldMap(), carId)
                .enqueue(new Callback<CarBleBean>() {
                    @Override
                    public void onResponse(Call<CarBleBean> call, Response<CarBleBean> response) {
                        if (response.isSuccessful() && response.body() != null) {
                            if (response.body().getStatus() == 200) {
                                try {
                                    if ("2".equals(response.body().getAttachment().getSupplierId())) {
                                        CarBleZXTBean carBleZXTBean = new Gson().fromJson(response.body().getAttachment().getBluttoothInfo().toString(), CarBleZXTBean.class);
                                        if (carBleZXTBean.getCars() != null) {
                                            //把蓝牙信息赋值到蓝牙工具类
                                            carBlePsw = carBleZXTBean.getCars().get(0).getPassword();
                                            carBleName = carBleZXTBean.getCars().get(0).getIdc();
                                            SPUtils.getInstance(mContext).putStringValue(SPKey.CAR_BLE_PSW, carBlePsw);
                                            SPUtils.getInstance(mContext).putStringValue(SPKey.BLE_MAC_IDC, carBleName);
                                            carType = ZHI_XIN_TONG;
                                        }
                                    } else if ("4".equals(response.body().getAttachment().getSupplierId())) {
                                        CarBleYunCheBean carBleYunCheBean = new Gson().fromJson(response.body().getAttachment().getBluttoothInfo().toString(), CarBleYunCheBean.class);
                                        if (carBleYunCheBean != null) {
                                            //把蓝牙信息赋值到蓝牙工具类
                                            carBlePsw = carBleYunCheBean.getBluetoothANAAA();
                                            carBleName = carBleYunCheBean.get_$AddrOfMACMAC26().replace(":", "").toUpperCase();
                                            SPUtils.getInstance(mContext).putStringValue(SPKey.CAR_BLE_PSW, carBlePsw);
                                            SPUtils.getInstance(mContext).putStringValue(SPKey.BLE_MAC_IDC, carBleName);
                                            carType = BleConstants.CarType.YUN_CHE;
                                        }
                                    } else if ("5".equals(response.body().getAttachment().getSupplierId())) {
                                        CarBsjBean carBsjBean = new Gson().fromJson(response.body().getAttachment().getBluttoothInfo().toString(), CarBsjBean.class);
                                        if (carBsjBean != null) {
                                            //把蓝牙信息赋值到蓝牙工具类
                                            carBlePsw = carBsjBean.getCarComputerPassword();
                                            carBleName = carBsjBean.getBlueMac().replace(":", "").toUpperCase();
                                            carBleKey = carBsjBean.getCarComputerSecret();
                                            SPUtils.getInstance(mContext).putStringValue(SPKey.CAR_BLE_PSW, carBlePsw);
                                            SPUtils.getInstance(mContext).putStringValue(SPKey.BLE_MAC_IDC, carBleName);
                                            SPUtils.getInstance(mContext).putStringValue(SPKey.CAR_BLE_KEY, carBleKey);
                                            carType = BleConstants.CarType.BO_SHI_JIE;
                                        }
                                    }
                                    Logger.d("bleinfo", "名字" + SPUtils.getInstance(mContext).getStringValue(SPKey.BLE_MAC_IDC) + "密码" + SPUtils.getInstance(mContext).getStringValue(SPKey.CAR_BLE_PSW));
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    Logger.i("bleinfo", "获取蓝牙信息接口错误");
                                }
                            } else {
                                Logger.i("bleinfo", "getBle" + response.body().getMessage() + "");
                            }
                        }
                        if (onGetBleResponse != null) {
                            onGetBleResponse.getBleResponse();
                        }
                    }

                    @Override
                    public void onFailure(Call<CarBleBean> call, Throwable t) {
                        if (onGetBleResponse != null) {
                            onGetBleResponse.getBleResponse();
                        }
                    }
                });
    }


    public interface OnGetBleResponse {
        void getBleResponse();
    }
}
